/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.utils;


import com.jspx.boot.environment.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jspx.json.JSONArray;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.util.*;


/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-1-7
 * Time: 19:04:35
 * com.jspx.utils.BeanUtil
 */
public class BeanUtil {
    private static final Logger log = LoggerFactory.getLogger(BeanUtil.class);

    private BeanUtil() {

    }

    /**
     * 设置属性,性能很差,但容错很好
     *
     * @param object bean对象
     * @param methodName 方法名称,如果存在set就不加set
     * @param obj    参数
     */
    public static void setSimpleProperty(Object object, String methodName, Object obj) {
        if (object == null) {
            throw new NullPointerException(object + " is NULL");
        }
        if (!StringUtil.hasLength(methodName)) return;
        if (methodName.contains(".")) methodName = methodName.substring(methodName.lastIndexOf("."));

        if (methodName.startsWith("void")) {
            methodName = methodName.substring(5).trim();
        } else if (!methodName.startsWith(ClassUtil.methodName_set)) {
            methodName = ClassUtil.methodName_set + StringUtil.capitalize(methodName);
        }

        Method method = ClassUtil.getDeclaredMethod(object.getClass(), methodName);
        if (method == null) {
            log.debug(object.getClass() + " setProperty not find method:" + methodName);
            throw new NullPointerException(object.getClass() + " setProperty not find method:" + methodName);
        }
        Type[] types = method.getGenericParameterTypes();
        if (types.length < 1) return;
        Type aType = types[0];

        Object[] pObject = new Object[1];
        try {
            pObject[0] = getTypeValue( obj,aType);
            if (SystemUtil.isAndroid()) {
                method.invoke(object, pObject);
            } else {
                (new java.beans.Expression(object, methodName, pObject)).execute();
            }
        } catch (Exception e) {
            log.error(object.getClass().getName() + "." + methodName + " setProperty  type=" + aType + " value=" + obj, e);
        }
    }


    public static Object getTypeValue(Object obj,Type aType)
    {
        Object result;
        if (ClassUtil.isArrayType(aType) && (obj != null && (obj.getClass().isArray() || obj instanceof JSONArray))) {
            Object[] vv;
            if (obj instanceof JSONArray) {
                vv = ((JSONArray) obj).toArray();
            } else {
                vv = (Object[]) obj;
            }
            if (aType.equals(int[].class))
                result = ArrayUtil.toIntArray(ArrayUtil.toStringArray(vv));
            else if (aType.equals(Integer[].class)) {
                result = ArrayUtil.toIntegerArray(ArrayUtil.toStringArray(vv));
            } else if (aType.equals(long[].class)) {
                result = ArrayUtil.toLongArray(ArrayUtil.toStringArray(vv));
            } else if (aType.equals(Long[].class)) {
                result = ArrayUtil.toLongObjectArray(ArrayUtil.toStringArray(vv));
            } else if (aType.equals(float[].class)) {
                result = ArrayUtil.toFloatArray(ArrayUtil.toStringArray(vv));
            } else if (aType.equals(Float[].class)) {
                result = ArrayUtil.toFloatObjectArray(ArrayUtil.toStringArray(vv));
            } else if (aType.equals(double[].class)) {
                result = ArrayUtil.toDoubleArray(ArrayUtil.toStringArray(vv));
            } else if (aType.equals(Double[].class)) {
                result = ArrayUtil.toDoubleObjectArray(ArrayUtil.toStringArray(vv));
            } else if (aType.equals(String[].class))
                result = ArrayUtil.toStringArray(vv);
            else
                result = obj;
        } else {
            if (aType.equals(Boolean.class) || aType.equals(boolean.class)) {
                result = ObjectUtil.toBoolean(obj);
            } else if (aType.equals(int.class) || aType.equals(Integer.class)) {
                result = ObjectUtil.toInt(obj);
            } else if (aType.equals(long.class) || aType.equals(Long.class)) {
                result = ObjectUtil.toLong(obj);
            } else if (aType.equals(float.class) || aType.equals(Float.class)) {
                result = ObjectUtil.toFloat(obj);
            } else if (aType.equals(double.class) || aType.equals(Double.class)) {
                result = ObjectUtil.toLong(obj);
            } else if (aType.equals(Date.class)) {
                result = ObjectUtil.toDate(obj);
            } else if (aType.equals(java.sql.Date.class)) {
                result = ObjectUtil.toSqlDate(obj);
            } else if (aType.equals(Timestamp.class)) {
                result = ObjectUtil.toSqlTimestamp(obj);
            } else if (aType.equals(String.class)) {
                result = obj;
            } else if (aType.equals(InputStream.class) || aType.equals(OutputStream.class)) {
                result = null;
            } else
                result = obj;
        }
        return result;
    }

    public static Object getProperty(Object object, String name) throws NoSuchMethodException {
        return getProperty(object, name, ArrayUtil.NULL, true);
    }


    /**
     * @param object    bean对象
     * @param name      方法名称
     * @param parameter 参数列表
     * @param jump      跳过不满足条件的方法,并且不会报错
     * @return 返回对象
     * @throws NoSuchMethodException 方法不能找到异常
     */
    public static Object getProperty(Object object, String name, Object[] parameter, boolean jump) throws NoSuchMethodException {
        if (!StringUtil.hasLength(name)) return null;
        if (object == null || ClassUtil.isStandardProperty(object.getClass())) {
            return object;
        }
        if (object instanceof Map && parameter == null) {
            Map map = (Map) object;
            return map.get(name);
        }
        Method[] methods = ClassUtil.getDeclaredMethods(object.getClass());
        Method testMethod = null;
        for (Method method : methods) {
            if (method.getGenericReturnType().equals(Void.TYPE)) continue;
            if (method.getName().equals(name) || method.getName().equals(ClassUtil.methodName_get + StringUtil.capitalize(name)) || method.getName().equals(ClassUtil.methodName_is + StringUtil.capitalize(name))) {
                //如果parameter == ArrayUtil.NULL 表示只起无参数的函数
                int iParam = method.getGenericParameterTypes().length;
                if (parameter != ArrayUtil.NULL && parameter.length == iParam || parameter == ArrayUtil.NULL && iParam == 0) {
                    testMethod = method;
                    break;
                }
            }
        }
        if (testMethod == null && jump) return null;
        if (testMethod == null) {
            throw new NoSuchMethodException(object.getClass().getName() + " method not find " + name);
        }
        try {
            if (testMethod.getParameterTypes().length == 0)
                return testMethod.invoke(object);
            else
                return testMethod.invoke(object, parameter);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(object.getClass().getName() + " getProperty=" + testMethod.getName() + "  parameter=" + Arrays.toString(parameter) + ",检查类中方法是否正常执行", e);
        }
        return null;
    }

    /**
     * 拷贝属性,
     *
     * @param getObj 得到属性的bean
     * @param copyTo 源bean
     */
    public static void copySimpleProperty(Object getObj, Object copyTo) {
        Method[] methodsGet = ClassUtil.getDeclaredMethods(getObj.getClass());
        Method[] methodsSet = ClassUtil.getDeclaredMethods(copyTo.getClass());
        for (Method setMethod : methodsSet) {
            if (setMethod.getName().startsWith(ClassUtil.methodName_set)) continue;
            if (setMethod.getGenericParameterTypes().length > 0) continue;
            try {
                for (Method method : methodsGet) {
                    if (!method.getName().startsWith(ClassUtil.methodName_set)) continue;
                    if (method.getName().substring(3).equals(setMethod.getName().substring(3)) && (setMethod.getName().startsWith(ClassUtil.methodName_get) || setMethod.getName().startsWith(ClassUtil.methodName_is))) {
                        method.invoke(getObj, setMethod.invoke(copyTo));
                        break;
                    }
                }
            } catch (Exception e) {
                log.error("error set methods " + setMethod.getName(), e);
            }
        }
    }

    /**
     * @param f1 方法数组1
     * @param f2 方法数组2
     * @return 合并两个方法数组
     */
    public static Method[] joinMethodArray(Method[] f1, Method[] f2) {
        if (f1 == null) return f2;
        if (f2 == null) return f1;
        Method[] result = new Method[f1.length + f2.length];
        for (int i = 0; i < result.length; i++) {
            if (i < f1.length)
                result[i] = f1[i];
            else
                result[i] = f2[i - f1.length];
        }
        return result;
    }


    public static Method[] appendMethodArray(Method[] f1, Method f2) {
        if (f1 == null && f2 == null) return null;
        if (f2 == null) return f1;
        if (f1 == null) {
            Method[] result = new Method[1];
            result[0] = f2;
            return result;
        }
        Method[] result = new Method[f1.length + 1];
        System.arraycopy(f1, 0, result, 0, f1.length);
        result[f1.length] = f2;
        return result;
    }


    /**
     * @param o bean 对象
     * @return Bean属性转换为Map对象
     * @throws Exception 异常
     */
    public static Map<String, Object> getMap(Object o) throws Exception {
        Map<String, Object> result = new HashMap<String, Object>();
        Field[] fields = ClassUtil.getDeclaredFields(o.getClass());
        for (Field field : fields) {
            if (field.getType().equals(Boolean.class)||field.getType().equals(boolean.class)) {
                result.put(field.getName(), getProperty(o, ClassUtil .methodName_is + StringUtil.capitalize(field.getName())));
            } else {
                result.put(field.getName(), getProperty(o, field.getName()));
            }
        }
        return result;
    }


    /**
     * @param xmlFile xml文件名称
     * @return XML文件转换为Bean
     * @throws IOException 读取错误
     */
    public static Object getObjectForXml(File xmlFile) throws IOException {
        if (!xmlFile.isFile()) return null;
        java.beans.XMLDecoder decoder = new java.beans.XMLDecoder(new FileInputStream(xmlFile));
        Object beanObject = decoder.readObject();
        decoder.close();
        return beanObject;
    }

    /**
     * @param aXml Creates object from xml
     * @return object from xml
     */
    public static Object getObjectForXml(String aXml) throws UnsupportedEncodingException {
        java.beans.XMLDecoder decoder = new java.beans.XMLDecoder(new BufferedInputStream(new ByteArrayInputStream(aXml.getBytes(Environment.defaultEncode))));
        Object obj = decoder.readObject();
        decoder.close();
        return obj;
    }

    /**
     * @param aObject Converts object to xml
     * @return xml representing an object
     */
    public static String toXml(Object aObject) {
        ByteArrayOutputStream baStream = new ByteArrayOutputStream();
        java.beans.XMLEncoder encoder = new java.beans.XMLEncoder(new BufferedOutputStream(baStream));
        encoder.writeObject(aObject);
        encoder.close();
        try {
            return baStream.toString(Environment.defaultEncode);
        } catch (UnsupportedEncodingException e) {

            e.printStackTrace();
        }
        return null;
    }


    /**
     *method.getModifiers()  返回说明
     *
     * PUBLIC: 1
     * PRIVATE: 2
     * PROTECTED: 4
     * STATIC: 8
     * FINAL: 16
     * SYNCHRONIZED: 32
     * VOLATILE: 64
     * TRANSIENT: 128
     * NATIVE: 256
     * INTERFACE: 512
     * ABSTRACT: 1024
     * STRICT: 2048
     *
     * @param o 对象
     * @return Map 对象的值映射成Map
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> toMap(Object o) {
        if (o == null) return new HashMap();
        if (o instanceof Class) {
            return new HashMap();
        }
        if (o instanceof AbstractMap) {
            return (Map<String, Object>) o;
        }
        Map<String, Object> valueMap = new HashMap<String, Object>();
        Method[] methods = ClassUtil.getDeclaredReturnMethods(o.getClass());
        if (methods != null)
            for (Method method : methods) {
                if ("toStirng".equals(method.getName())) continue;
                if (method.getGenericParameterTypes().length > 0) continue;
                if (method.getModifiers() != 1) continue;
                try {
                    valueMap.put(ClassUtil.getCallMethodName(method), getProperty(o, method.getName()));
                } catch (Exception e) {
                    log.error(o + "   method=" + method.getName(), e);
                }
            }
        return valueMap;
    }

    public static String toString(Object o) {
        Map map = toMap(o);
        StringBuilder sb = new StringBuilder();
        for (Object obj : map.values()) {
            sb.append(obj).append(" ");
        }
        return sb.toString();
    }

    /**
     * @param object bean对象
     * @param string 调用一个方法
     * @param args   参数
     * @throws Exception 运行错误
     */
    public static Object invoke(Object object, String string, Object... args) throws Exception {

        if (object == null || string == null) return null;
        if (args == null) {
            Method method = object.getClass().getMethod(string);
            if (method != null) method.invoke(object);
        } else {
            if (SystemUtil.isAndroid()) {
                Class<?>[] las = ClassUtil.getClassArray(args);
                Method method = object.getClass().getMethod(string, las);
                if (method != null) return method.invoke(object, args);
            } else {
                java.beans.Expression expr = new java.beans.Expression(object, string, args);
                expr.execute();
                return expr.getValue();
            }
        }
        return null;
    }

    /**
     * @param o 对象
     * @return 判断是否对象为一个集合，列表 类型
     */
    public static boolean isArraySet(Object o)
    {
        return  o!=null&&(o.getClass().isArray() || o instanceof Collection || o instanceof Map);
    }



}